﻿using System;
using System.Configuration;
using DbHelper.Implementation;
using DbHelper.Resources;

namespace DbHelper
{
    /// <summary>
    /// Classe helper que implementa o pattern Singleton para obter instâncias de Db.
    /// </summary>
    /// <remarks>Lucas Damiani</remarks> 
    public static class DbFactory
    {
        #region Constantes
        /// <summary>
        /// Representa a connection string default utilizada caso nenhuma seja fornecida. Chave utilizada na seção ConnectionStrings do arquivo de configuração da aplicação.
        /// </summary>
        /// <remarks>Lucas Damiani</remarks>
        private const string CONNECTION_STRING_KEY = "DefaultConnectionString";

        /// <summary>
        /// Representa a implementação de banco de dados que será utilizada. Chave utilizada na seção AppSettings do arquivo de configuração da aplicação.
        /// </summary>
        /// <remarks>Lucas Damiani</remarks>
        private const string DB_IMPLEMENTATION_KEY = "DbImplementation";
        #endregion

        #region Atributos
        /// <summary>
        /// Instância de Db, utilizada pela implementação do pattern Singleton.
        /// </summary>
        /// <remarks>Lucas Damiani</remarks>
        private static Db db;
        #endregion

        #region Métodos
        /// <summary>
        /// Busca uma instância singleton de Db, de acordo com a implementação e connection string descrita no arquivo de configuração.
        /// </summary>
        /// <returns>Instância singleton de Db.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public static Db GetInstance()
        {
            if (db == null)
            {
                db = GetDb(null, DbImplentation.Unknown);
            }
            return db;
        }

        /// <summary>
        /// Busca uma instância singleton de Db, de acordo com a implementação descrita no arquivo de configuração.
        /// </summary>
        /// <param name="connectionString">String de conexão.</param>
        /// <returns>Instância singleton de Db.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public static Db GetInstance(string connectionString)
        {
            if (db == null)
            {
                db = GetDb(connectionString, DbImplentation.Unknown);
            }
            return db;
        }

        /// <summary>
        /// Busca uma instância singleton de Db.
        /// </summary>
        /// <param name="connectionString">String de conexão.</param>
        /// <param name="dbImplementation">Implementação do banco de dados.</param>
        /// <returns>Instância singleton de Db.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public static Db GetInstance(string connectionString, DbImplentation dbImplementation)
        {
            if (db == null)
            {
                db = GetDb(connectionString, dbImplementation);
            }
            return db;
        }

        /// <summary>
        /// Busca uma instância de Db, de acordo com a implementação e connection string descrita no arquivo de configuração.
        /// </summary>
        /// <returns>Instância de Db.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public static Db GetInstanceByValue()
        {
            return GetDb(null, DbImplentation.Unknown);
        }

        /// <summary>
        /// Busca uma instância de Db, de acordo com a implementação descrita no arquivo de configuração.
        /// </summary>
        /// <param name="connectionString">String de conexão.</param>
        /// <returns>Instância de Db.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public static Db GetInstanceByValue(string connectionString)
        {
            return GetDb(connectionString, DbImplentation.Unknown);
        }

        /// <summary>
        /// Busca uma instância de Db.
        /// </summary>
        /// <param name="connectionString">String de conexão.</param>
        /// <param name="dbImplementation">Implementação do banco de dados.</param>
        /// <returns>Instância de Db.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public static Db GetInstanceByValue(string connectionString, DbImplentation dbImplementation)
        {
            return GetDb(connectionString, dbImplementation);
        }

        /// <summary>
        /// Cria uma nova instância de Db.
        /// </summary>
        /// <param name="connectionString">String de conexão. Se "null", tenta buscar informação do arquivo de configuração.</param>
        /// <param name="dbImplementation">Implentação do banco de dados. Se "DbImplementation.Unknown", tenta buscar informação do arquivo de configuração.</param>
        /// <exception cref="DbException" />
        /// <remarks>Lucas Damiani</remarks>
        private static Db GetDb(string connectionString, DbImplentation dbImplementation)
        {
            Db dbTmp = null;
            string connString = connectionString;
            if (connString == null)
            {
                connString = ConfigurationManager.ConnectionStrings[CONNECTION_STRING_KEY].ConnectionString;
            }
            DbImplentation dbImpl = dbImplementation;
            if (dbImpl.Equals(DbImplentation.Unknown))
            {
                string dbImplStr = ConfigurationManager.AppSettings[DB_IMPLEMENTATION_KEY];
                if (!Enum.IsDefined(typeof(DbImplentation), dbImplStr))
                {
                    throw new DbException(DbHelperResources.UNIMPLEMENTED_DATABASE);
                }

                dbImpl = (DbImplentation)Enum.Parse(typeof(DbImplentation), dbImplStr, true);
            }

            switch (dbImpl)
            {
                case DbImplentation.MySQL:
                    dbTmp = new MySqlDb(connString);
                    break;
                case DbImplentation.SqlServer:
                    dbTmp = new SqlDb(connString);
                    break;
                case DbImplentation.Oracle:
                    break;
            }

            return dbTmp;
        }
        #endregion
    }
}
